#ifndef MODULATOR_H
#define MODULATOR_H

#include "win_raw_sound_ostream.h"
#include <vector>
#include "chronometer.h"
#include <climits>
#include <algorithm>

namespace hard {

struct modulator
{
//  typedef signed char sample_type;
//  static const sample_type max_abs_amplitude = SCHAR_MAX/2;
  typedef short sample_type;
  static const sample_type max_abs_amplitude = SHRT_MAX/2;

private:
  win_raw_sound_ostream out;
  double w_;
  double amplitude_;
  size_t write_period;
  std::vector<sample_type> samples_;
  DWORD nSamplesPerSec;
  HANDLE thread;
  HANDLE endThread;
  bool end;
  unsigned int threadId;
  HANDLE timer;
  //HANDLE timer_queue;
  mutable CRITICAL_SECTION crit_w;
  mutable CRITICAL_SECTION crit_amplitude;
  mutable CRITICAL_SECTION crit_samples;
  chronometer chr;
  mutable bool destroy;

  const char* data_to_write() const
  {
    return reinterpret_cast<const char*>(&(samples_[0]));
  }

  //friend VOID CALLBACK TimerCallback(PVOID lpParam, BOOLEAN TimerOrWaitFired);
  friend unsigned int WINAPI modulator_thread(void* arg);

public:
  const static double pi;
  const static double next_note_factor;
  const static double freq_A;
  const static double freq_As;
  const static double freq_B;
  const static double freq_C;
  const static double freq_Cs;
  const static double freq_D;
  const static double freq_Ds;
  const static double freq_E;
  const static double freq_F;
  const static double freq_Fs;
  const static double freq_G;
  const static double freq_Gs;

  modulator(size_t write_period=100, double carrier_freq=freq_A, double carrier_amplitude=1., DWORD nSamplesPerSec=44100);
  modulator(const modulator& m)
  : out(m.out), w_(m.w_), amplitude_(m.amplitude_), write_period(m.write_period), 
    samples_(m.samples_),
    nSamplesPerSec(m.nSamplesPerSec), end(false), chr(m.write_period), destroy(true)
  {
    m.destroy=false;
  }
  ~modulator();

  std::vector<sample_type> samples() const
  {
    std::vector<sample_type> ret;
    EnterCriticalSection(&crit_samples);
    ret = samples_;
    LeaveCriticalSection(&crit_samples);
    return ret;
  }
  void write_samples()
  {
    EnterCriticalSection(&crit_samples);
    out.write(&(samples_[0]), sizeof(sample_type)*samples_.size());
    LeaveCriticalSection(&crit_samples);
  }
  void update_samples();

  void set_w(double w)
  {
    EnterCriticalSection(&crit_w);
    this->w_ = w;
    LeaveCriticalSection(&crit_w);
  }
  double get_w() const
  {
    double ret;
    EnterCriticalSection(&crit_w);
    ret = w_;
    LeaveCriticalSection(&crit_w);
    return ret;
  }

  void set_amplitude(double amp)
  {
    EnterCriticalSection(&crit_w);
    this->amplitude_ = min(max(amp,0.), 1.);
    LeaveCriticalSection(&crit_w);
  }
  double get_amplitude() const
  {
    double ret;
    EnterCriticalSection(&crit_w);
    ret = amplitude_;
    LeaveCriticalSection(&crit_w);
    return ret;
  }

  void set_freq(double freq)
  {
    set_w(freq*pi*2);
  }
  double get_freq() const
  {
    return get_w()/(pi*2);
  }

  chronometer get_chronometer() const
  {
    return chr;
  }

  const win_raw_sound_ostream& get_win_raw_sound_ostream() const
  {
    return out;
  }
};

}
#endif
